package main

import (
	"fmt"
)

/*
 go 中的实现接口的方式就是让结构体绑定接口的方法
*/

//声明/定义一个接口
type Usb interface {
	//声明了两个没有实现的方法
	Start()
	Stop()
}

//声明/定义一个接口
type Usb2 interface {
	//声明了两个没有实现的方法
	Start()
	Stop()
	Test()
}

type Phone struct {
}

//让Phone 实现 Usb接口的方法
func (p Phone) Start() {
	fmt.Println("手机开始工作。。。")
}
func (p Phone) Stop() {
	fmt.Println("手机停止工作。。。")
}

type Camera struct {
}

//让Camera 实现   Usb接口的方法
func (c Camera) Start() {
	fmt.Println("相机开始工作~~~。。。")
}
func (c Camera) Stop() {
	fmt.Println("相机停止工作。。。")
}

//计算机
type Computer struct {
}

//编写一个方法Working 方法，接收一个Usb接口类型变量
//只要是实现了 Usb接口 （所谓实现Usb接口，就是指实现了 Usb接口声明所有方法）
func (c Computer) Working(usb Usb) {

	//通过usb接口变量来调用Start和Stop方法
	usb.Start()
	usb.Stop()
}

//5) 只要是自定义数据类型，就可以实现接口，不仅仅是结构体类型

type Run interface {
	Run()
}

type myInt int

func (mi myInt) Run() {
	fmt.Println("跑步~~~")
}

//8) 一个接口C可以继承多个接口A、B等。。。，但是A接口的实现类必须实现所有的实现的接口的方法
type A interface {
	B
	C
}

type B interface {
	BDO()
}
type C interface {
	CDO()
}

// 一个结构体实现A接口
type AImpl struct {
}

// 实现B接口方法
func (ai AImpl) BDO() {
	fmt.Println("AImpl BDO ...")
}

// 实现C接口方法
func (ai AImpl) CDO() {
	fmt.Println("AImpl CDO ...")
}

type MyInterface interface{}
type Stu struct {
	Name string
}

func main() {

	//测试
	//先创建结构体变量
	computer := Computer{}
	phone := Phone{}
	camera := Camera{}

	//关键点
	computer.Working(phone)
	computer.Working(camera)
	fmt.Println("~~~~~~~~~~分割线~~~~~~~")
	// =================================
	//1) 接口本身不能创建实例对象，但是可以指向一个实现了该接口自定义类型的变量
	var usb1 Usb
	usb1 = phone
	usb1.Start() // 手机开始工作。。。

	//2) 接口中所有的方法不允许有实现

	//3) 一个自定义类型需要将某个接口的所有方法都实现，我们说这个自定义类型实现了该接口

	//4) 一个自定义类型只有实现了某个接口，才能将该自定义类型的实例(变量),赋值给接口类型

	//5) 只要是自定义数据类型，就可以实现接口，不仅仅是结构体类型
	fmt.Println("~~~~~~~~~~分割线~~~~~~~")
	var mi myInt = 10
	mi.Run() //跑步~~~

	//6) 一个自定义类型可以实现多个接口，同时必须实现所有的接口的方法

	//7) 接口中不能有任何变量（go语言的特性，这一点和其他语言不一样）

	//8) 一个接口C可以继承多个接口A、B等。。。，但是A接口必须实现所有的实现的接口的方法
	fmt.Println("~~~~~~~~~~分割线~~~~~~~")
	var ai AImpl = AImpl{}
	ai.BDO() //AImpl BDO ...
	ai.CDO() //AImpl CDO ...

	//9) 接口默认是一个引用类型（指针类型），如果没有对interface进行初始化那么输出一个nil

	//10) 空接口 interface{} 没有任何方法，所有的类型都实现了空接口，我们可以吧任何一个变量赋值给空接口
	fmt.Println("~~~~~~~~~~分割线~~~~~~~")
	var blank interface{}
	var i int = 10
	fmt.Printf("blank = %v\n", blank)      //blank = <nil>
	fmt.Printf("&blank = %v\n", &blank)    //&blank = 0xc0000401c0
	fmt.Printf("blank type = %T\n", blank) //blank type = <nil>
	// fmt.Printf("*blank = %v\n", *blank)
	fmt.Println("i = ", i)
	blank = i
	fmt.Printf("&blank = %v, &i = %v\n", &blank, &i) //&blank = 0xc0000321d0, &i = 0xc0000100b0
	fmt.Printf("blank = %v\n", blank)                //blank = 100
	fmt.Printf("&blank = %v\n", &blank)              //&blank = 0xc0000401c0
	fmt.Printf("blank type = %T\n", blank)           //blank type = int
	// fmt.Printf("*blank = %v\n", *blank) // blank 不是一个指针类型，但是他有地址，该地址的寄存器中存放的是i的值
	blank = 1000
	fmt.Println("~i = ", i)
	fmt.Println("~~~~~~~~~~分割线~~~~~~~")
	var b2 MyInterface
	var s1 Stu = Stu{"小王"}
	b2 = s1
	fmt.Println("s1.Name = ", s1.Name) //s1.Name =  小王
	// b2.Name = "小刘"
	// fmt.Println("s1.Name ~", s1.Name)
	fmt.Printf("b2 = %v, b2 type = %T\n", b2, b2) //b2 = {小王}, b2 type = main.Stu
	fmt.Printf("&b2 = %v, &s1 = %v\n", &b2, &s1)  //&b2 = 0xc0000401d0, &s1 = &{小王}
	fmt.Println("~~~~~~~~~~分割线~~~~~~~")

}
